package network.client;

import graphics.Shape;
import graphics.ShapeFactory;

import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;

import javax.swing.JOptionPane;

import network.Message;
import network.MessageFactory;
import network.MessageQueue;
import network.MessageReceiver;
import network.MessageType;
import controller.Constants;
import controller.DrawingController;
import controller.NetworkController;
import data.ChatMessages;
import data.ShapesData;
import data.UserList;
import data.WorkingMode;
import data.WorkingMode.MODE;

/**
 * A witeboard client object contains some methods to connect and communicate
 * with the server.
 * 
 * @author Jie Zhao(288654)
 * 
 */
public class WhiteboardClient {
	Socket clientSocket = null;
	Thread clientThread = null;
	String reason = null;
	MessageReceiver receiver = new MessageReceiver();
	MessageQueue queue = new MessageQueue();
	boolean isRunning = true;

	/**
	 * connect to a server
	 * 
	 * @param serverAddress
	 * @param port
	 * @param username
	 * @param password
	 * @return
	 */
	public boolean connect(String serverAddress, int port, String username,
			String password) {
		if (Constants.DEBUG)
			System.out.println("Client is connecting to " + serverAddress + ":"
					+ port);
		try {
			Socket s = new Socket(serverAddress, port);
			clientSocket = s;
			try {
				Message m = MessageFactory.createLoginMessage(username,
						password);
				NetworkController.getInstance().sendMessage(m);

			} catch (Exception e) {
				if (Constants.DEBUG)
					e.printStackTrace();
			}

			clientThread = new Thread(new ClientThread());
			clientThread.start();
			new MessageProcessor().start();
			try {
				Message mm = MessageFactory.createDrawSyncReqMessage();
				NetworkController.getInstance().sendMessage(mm);

			} catch (Exception e) {
				if (Constants.DEBUG)
					e.printStackTrace();
			}

			return true;
		} catch (UnknownHostException e) {
			if (Constants.DEBUG)
				e.printStackTrace();
			reason = e.getMessage();
		} catch (IOException e) {
			if (Constants.DEBUG)
				e.printStackTrace();
			reason = e.getMessage();
		}
		return false;
	}

	private class ClientThread implements Runnable {

		@Override
		public void run() {
			try {
				while (!clientSocket.isClosed() && isRunning) {
					Message m = receiver.receiveMessage(clientSocket);
					queue.putMessage(m);
				}
			} catch (IOException e) {
				JOptionPane.showMessageDialog(null,
						"You are disconnected from the server!@_@");
				if (Constants.DEBUG)
					e.printStackTrace();
			} catch (Exception ee) {
				JOptionPane.showMessageDialog(null,
						"You are kicked by the server!");
				if (Constants.DEBUG)
					ee.printStackTrace();
			}
		}
	}

	class MessageProcessor extends Thread {
		@Override
		public void run() {
			while (true) {
				Message msg = null;
				try {
					msg = queue.takeMessage();
				} catch (Exception e) {
					if (Constants.DEBUG)
						e.printStackTrace();
				}

				switch (msg.getType()) {
				case MessageType.CHECK_ALIVE:
					break;
				case MessageType.CLEAR:
					ShapesData.getInstance().clear();
					DrawingController.getInstance().repaint();
					break;
				case MessageType.LOGIN_REJECT:
					isRunning = false;
					JOptionPane
							.showMessageDialog(null,
									"Your Connection request has been rejected by the server!");

					try {
						clientSocket.close();
					} catch (Exception e) {
						// TODO: handle exception
					}
					WorkingMode.getInstance().changeWorkingMode(MODE.LOCAL);
					return;
				case MessageType.USERNAME_SYNC:
					String list = msg.getMessageBody();
					UserList.getInstance().clear();
					String[] str = list.split(",");
					for (int i = 0; i < str.length; i++) {
						if (str[i] != null && !"".equals(str[i]))
							UserList.getInstance().addClient(str[i]);
					}
					// UserList.getInstance().print();
					break;
				case MessageType.DRAW_SYNC:
					String[] shapes = msg.getMessageBody().split("`");
					for (String s : shapes) {
						if (s != null && !"".equals(s)) {
							Shape ss = ShapeFactory.createShapeByString(s);
							ss.fromString(s);
							ShapesData.getInstance().addShape(ss);
						}
					}
					break;
				case MessageType.DRAW:
					String data = msg.getMessageBody();
					Shape s = ShapeFactory.createShapeByString(data);
					s.fromString(data);
					ShapesData.getInstance().addShape(s);
					break;
				case MessageType.CHAT:
					String data1 = msg.getMessageBody();
					String[] d = data1.split(":");
					ChatMessages.getInstance().addMessage(d[0], d[1]);
					break;
				default:
					break;
				}
			}
		}
	}

	public void close() {
		try {
			clientSocket.close();
			clientThread.interrupt();
		} catch (Exception e) {
			// e.printStackTrace();
		}
	}

	public static void main(String[] args) {
	}

	public String getReason() {
		return reason == null ? "" : reason;
	}

	public Socket getClientSocket() {
		return clientSocket;
	}
}
